﻿using Engine;
using Engine.Graphics;
using SharpDX;
using SharpDX.Direct3D11;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;

namespace Cobra11Undercover.Material
{
    public class C11Material : Engine.Graphics.IMaterial
    {
        #region Private Fields

        private int bumpTextureID1;
        private int bumpTextureID2;
        private int bumpTextureID3;
        private string bumpTextureName1;

        private string bumpTextureName2;

        private string bumpTextureName3;

        private uint hashCode;

        private EnumMappingMode mappingMode;

        private MappingsWR2 mappingsWr2_1;

        private MappingsWR2 mappingsWr2_2;

        private MappingsWR2 mappingsWr2_3;

        private MaterialManager materialManager;
        private string textureName1;

        private string textureName2;

        private string textureName3;

        #endregion Private Fields

        #region Public Constructors

        /// <summary>
        /// Creates a Material. Texture names should be given without fileextension.
        /// </summary>
        /// <param name="TextureName1"></param>
        /// <param name="TextureName2"></param>
        /// <param name="TextureName3"></param>
        /// <param name="mappings"></param>
        public C11Material(string TextureName1, string TextureName2, string TextureName3, EnumMappingMode mappings,
            MaterialManager materialManager)
            : this(TextureName1, TextureName2, TextureName3, string.Empty, string.Empty, string.Empty, mappings,
                  Vector4.Zero, Vector4.Zero, materialManager)
        {
        }

        public C11Material(C11Material material)
        {
            this.hashCode = 0;
            this.textureName1 = material.textureName1;
            this.textureName2 = material.textureName2;
            this.textureName3 = material.textureName3;

            this.bumpTextureName1 = material.bumpTextureName1;
            this.bumpTextureName2 = material.bumpTextureName2;
            this.bumpTextureName3 = material.bumpTextureName3;

            this.materialManager = material.materialManager;
            // Update textureIds
            UpdateTextureIDs();

            this.mappingsWr2_1 = material.mappingsWr2_1;
            this.mappingsWr2_2 = material.mappingsWr2_2;
            this.mappingsWr2_3 = material.mappingsWr2_3;
            this.mappingMode = material.mappingMode;
            UpdateHashCode();
        }

        public C11Material(int TextureID1, int TextureID2, int TextureID3, EnumMappingMode enumMappingMode, MaterialManager materialManager)
            : this(materialManager.TextureNames[TextureID1], materialManager.TextureNames[TextureID2], materialManager.TextureNames[TextureID3],
                  enumMappingMode, materialManager)
        {
        }

        public C11Material(int TextureID1, int TextureID2, int TextureID3, EnumMappingMode enumMappingMode, MaterialManager materialManager,
            MappingsWR2 mappingTexture1, MappingsWR2 mappingTexture2, MappingsWR2 mappingTexture3)
            : this(materialManager.TextureNames[TextureID1], materialManager.TextureNames[TextureID2], materialManager.TextureNames[TextureID3],
                  enumMappingMode, mappingTexture1, mappingTexture2, mappingTexture3, materialManager)
        {
        }

        public C11Material(string TextureName1, string TextureName2, string TextureName3, string bumpTextureName1, string bumpTextureName2,
            string bumpTextureName3, EnumMappingMode mappings, Vector4 texMod1, Vector4 texMod2, MaterialManager materialManager)
        {
            this.materialManager = materialManager;
            this.mappingMode = mappings;
            this.textureName1 = TextureName1;
            this.textureName2 = TextureName2;
            this.textureName3 = TextureName3;

            this.bumpTextureName1 = bumpTextureName1;
            this.bumpTextureName2 = bumpTextureName2;
            this.bumpTextureName3 = bumpTextureName3;

            this.mappingsWr2_1 = new MappingsWR2(texMod1, texMod2);
            this.mappingsWr2_2 = new MappingsWR2();
            this.mappingsWr2_3 = new MappingsWR2();

            // Update textureIds
            UpdateTextureIDs();

            this.hashCode = 0;
            UpdateHashCode();
        }

        public C11Material(string TextureName1, string TextureName2, string TextureName3, EnumMappingMode mappings,
            MappingsWR2 mappingTexture1, MappingsWR2 mappingTexture2, MappingsWR2 mappingTexture3, MaterialManager materialManager)
            : this(TextureName1, TextureName2, TextureName3, mappings, materialManager)
        {
            this.mappingsWr2_1 = mappingTexture1;
            this.mappingsWr2_2 = mappingTexture2;
            this.mappingsWr2_3 = mappingTexture3;

            UpdateHashCode();
        }

        #endregion Public Constructors

        #region Public Properties

        public string BumpTextureName1
        {
            get
            {
                return this.bumpTextureName1;
            }
            set
            {
                this.bumpTextureName1 = value;
                UpdateHashCode();
            }
        }

        public string BumpTextureName2
        {
            get
            {
                return this.bumpTextureName2;
            }
            set
            {
                this.bumpTextureName2 = value;
                UpdateHashCode();
            }
        }

        public string BumpTextureName3
        {
            get
            {
                return this.bumpTextureName3;
            }
            set
            {
                this.bumpTextureName3 = value;
                UpdateHashCode();
            }
        }

        public EnumMappingMode MappingMode
        {
            get
            {
                return this.mappingMode;
            }
            set
            {
                this.mappingMode = value;
            }
        }

        int IMaterial.MappingMode
        {
            get
            {
                return (int)this.mappingMode;
            }

            set
            {
                this.mappingMode = (EnumMappingMode)value;
            }
        }

        public MappingsWR2 Mappings1
        {
            get
            {
                return this.mappingsWr2_1;
            }
            set
            {
                this.mappingsWr2_1 = value;
                UpdateHashCode();
            }
        }

        public MappingsWR2 Mappings2
        {
            get
            {
                return this.mappingsWr2_2;
            }
            set
            {
                this.mappingsWr2_2 = value;
                UpdateHashCode();
            }
        }

        public MappingsWR2 Mappings3
        {
            get
            {
                return this.mappingsWr2_3;
            }
            set
            {
                this.mappingsWr2_3 = value;
                UpdateHashCode();
            }
        }

        public int TextureID1 { get; set; }

        public int TextureID2 { get; set; }

        public int TextureID3 { get; set; }

        public string TextureName1
        {
            get
            {
                return this.textureName1;
            }
            set
            {
                this.textureName1 = value;
                UpdateHashCode();
            }
        }

        public string TextureName2
        {
            get
            {
                return this.textureName2;
            }
            set
            {
                this.textureName2 = value;
                UpdateHashCode();
            }
        }

        public string TextureName3
        {
            get
            {
                return this.textureName3;
            }
            set
            {
                this.textureName3 = value;
                UpdateHashCode();
            }
        }

        #endregion Public Properties

        #region Public Methods

        public static C11Material FromStream(BinaryReader br, MaterialManager materialManager)
        {
            C11Material material = new C11Material(br.ReadString(),
                                             br.ReadString(),
                                             br.ReadString(),
                                            (EnumMappingMode)br.ReadByte(),
                                             MappingsWR2.FromStream(br),
                                             MappingsWR2.FromStream(br),
                                             MappingsWR2.FromStream(br),
                                             materialManager
                                             );

            //material.MappingMode = EnumMappingMode.material00;
            material.UpdateHashCode();

            return material;
        }

        public static C11Material FromXmlStream(XmlTextReader reader, MaterialManager materialManager)
        {
            while (reader.Read())
            {
                //throw new NotImplementedException("c11 uc XML-material loading is not implemented!");
                if (reader.NodeType == XmlNodeType.Element
                        && reader.Name == "C11UCMaterial")
                {
                    string textureName1 = reader.GetAttribute("Texture1");
                    string textureName2 = reader.GetAttribute("Texture2");
                    string textureName3 = reader.GetAttribute("Texture3");
                    string bumpTextureName1 = reader.GetAttribute("BumpTexture1");
                    string bumpTextureName2 = reader.GetAttribute("BumpTexture2");
                    string bumpTextureName3 = reader.GetAttribute("BumpTexture3");
                    EnumMappingMode mappingMode = (EnumMappingMode)Convert.ToInt32(reader.GetAttribute("MappingMode"));
                    MappingsWR2 mappings1;
                    MappingsWR2 mappings2;
                    MappingsWR2 mappings3;
                    Vector4 mappingVectror1 = Vector4.Zero;
                    Vector4 mappingVectror2 = Vector4.Zero;
                    int currentMapping = 1;
                    while (reader.Read() && ((reader.Name != "Material") && (reader.NodeType != XmlNodeType.EndElement)))
                    {
                        if (reader.Name == "MaterialMapping" && currentMapping == 1)
                        {
                            mappings1 = MappingsWR2.FromXmlReader(reader);
                            mappingVectror1 = mappings1.mappings1;
                            mappingVectror2 = mappings1.mappings2;
                            currentMapping++;
                            continue;
                        }
                        if (reader.Name == "MaterialMapping" && currentMapping == 2)
                        {
                            mappings2 = MappingsWR2.FromXmlReader(reader);
                            currentMapping++;
                            continue;
                        }
                        if (reader.Name == "MaterialMapping" && currentMapping == 3)
                        {
                            mappings3 = MappingsWR2.FromXmlReader(reader);
                            currentMapping++;
                            continue;
                        }
                    }
                    C11Material material = new C11Material(textureName1, textureName2, textureName3, bumpTextureName1, bumpTextureName2, bumpTextureName3, mappingMode,
                        mappingVectror1, mappingVectror2, materialManager);

                    return material;
                }
            }
            throw new Exception("No Material found at the position in xml-file: " + reader.Name);
        }

        public Vector4 GetMappings1A()
        {
            return this.mappingsWr2_1.mappings1;
        }

        public Vector4 GetMappings1B()
        {
            return this.mappingsWr2_1.mappings2;
        }

        public Vector4 GetMappings2A()
        {
            return this.mappingsWr2_2.mappings1;
        }

        public Vector4 GetMappings2B()
        {
            return this.mappingsWr2_2.mappings2;
        }

        public Vector4 GetMappings3A()
        {
            return this.mappingsWr2_3.mappings1;
        }

        public Vector4 GetMappings3B()
        {
            return this.mappingsWr2_3.mappings2;
        }

        public uint GetMaterialHashCode()
        {
            return this.hashCode;
        }

        public string[] GetUsedTextureNames()
        {
            List<string> usedTextureNames = new List<string>();

            if (!usedTextureNames.Contains(this.textureName1))
                usedTextureNames.Add(this.textureName1);
            if (!usedTextureNames.Contains(this.textureName2))
                usedTextureNames.Add(this.textureName2);
            if (!usedTextureNames.Contains(this.textureName3))
                usedTextureNames.Add(this.textureName3);
            if (!usedTextureNames.Contains(this.bumpTextureName1))
                usedTextureNames.Add(this.bumpTextureName1);
            if (!usedTextureNames.Contains(this.bumpTextureName2))
                usedTextureNames.Add(this.bumpTextureName2);
            if (!usedTextureNames.Contains(this.bumpTextureName3))
                usedTextureNames.Add(this.bumpTextureName3);

            usedTextureNames.Remove(string.Empty);

            return usedTextureNames.ToArray();
        }

        public void SaveToStream(BinaryWriter bw)
        {
            bw.Write(Path.GetFileNameWithoutExtension(this.textureName1));
            bw.Write(Path.GetFileNameWithoutExtension(this.textureName2));
            bw.Write(Path.GetFileNameWithoutExtension(this.textureName3));
            bw.Write((byte)this.mappingMode);
            this.mappingsWr2_1.SaveToStream(bw);
            this.mappingsWr2_2.SaveToStream(bw);
            this.mappingsWr2_3.SaveToStream(bw);
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("T1: " + this.textureName1);
            sb.Append(" T2: " + this.textureName2);
            sb.Append(" T3: " + this.textureName3);
            //sb.Append("  Tex1: " + Path.GetFileNameWithoutExtension(MaterialManager.TexturePaths[wr2Surface.texture1ID]));
            //sb.Append("  Tex2: " + Path.GetFileNameWithoutExtension(MaterialManager.TexturePaths[wr2Surface.texture2ID]));
            //sb.Append("  Tex3: " + Path.GetFileNameWithoutExtension(MaterialManager.TexturePaths[wr2Surface.texture3ID]));
            sb.Append(" " + ((EnumMappingMode)this.mappingMode).ToString());
            return sb.ToString();
        }

        public string ToXml(XmlTextWriter writer)
        {
            // writer.WriteComment("Material for Scenery Editor V1.0");

            writer.WriteStartElement("C11UCMaterial");

            writer.WriteAttributeString("Texture1", Path.GetFileNameWithoutExtension(this.textureName1));
            writer.WriteAttributeString("BumpTexture1", Path.GetFileNameWithoutExtension(this.bumpTextureName1));
            writer.WriteAttributeString("Texture2", Path.GetFileNameWithoutExtension(this.textureName2));
            writer.WriteAttributeString("BumpTexture2", Path.GetFileNameWithoutExtension(this.bumpTextureName2));
            writer.WriteAttributeString("Texture3", Path.GetFileNameWithoutExtension(this.textureName3));
            writer.WriteAttributeString("BumpTexture3", Path.GetFileNameWithoutExtension(this.bumpTextureName3));

            writer.WriteAttributeString("MappingMode", ((byte)this.mappingMode).ToString());

            this.mappingsWr2_1.ToXmlStream(writer);
            this.mappingsWr2_2.ToXmlStream(writer);
            this.mappingsWr2_3.ToXmlStream(writer);

            writer.WriteEndElement();

            return "C11 Material";
        }

        public void ToXmlStream(XmlWriter writer)
        {
            throw new NotImplementedException("C11 material to xmlStream not complete implemented!");
            writer.WriteComment("Material for Scenery Editor V1.0");

            writer.WriteStartElement("Material");

            writer.WriteAttributeString("Texture1", Path.GetFileNameWithoutExtension(this.textureName1));
            writer.WriteAttributeString("Texture2", Path.GetFileNameWithoutExtension(this.textureName2));
            writer.WriteAttributeString("Texture3", Path.GetFileNameWithoutExtension(this.textureName3));

            writer.WriteAttributeString("MappingMode", ((byte)this.mappingMode).ToString());

            this.mappingsWr2_1.ToXmlStream(writer);
            this.mappingsWr2_2.ToXmlStream(writer);
            this.mappingsWr2_3.ToXmlStream(writer);

            writer.WriteEndElement();
        }

        #endregion Public Methods

        #region Internal Methods

        internal ShaderResourceView GetBumpTexture1(MaterialManager materialManager)
        {
            int index = materialManager.GetTextureID(this.bumpTextureName1);
            if (index > -1)
            {
                //index = 109;
                return materialManager.Textures[index];
            }
            return materialManager.DummyNormalMap;
        }

        internal ShaderResourceView GetBumpTexture2(MaterialManager materialManager)
        {
            int index = materialManager.GetTextureID(this.bumpTextureName2);
            if (index > -1)
            {
                //index = 109;
                return materialManager.Textures[index];
            }
            return materialManager.DummyNormalMap;
        }

        internal ShaderResourceView GetBumpTexture3(MaterialManager materialManager)
        {
            int index = materialManager.GetTextureID(this.bumpTextureName3);
            if (index > -1)
            {
                //index = 109;
                return materialManager.Textures[index];
            }
            return materialManager.DummyNormalMap;
        }

        internal ShaderResourceView GetTexture1(MaterialManager materialManager)
        {
            return materialManager.Textures[materialManager.GetTextureID(this.textureName1)];
        }

        internal ShaderResourceView GetTexture2(MaterialManager materialManager)
        {
            return materialManager.Textures[materialManager.GetTextureID(this.textureName2)];
        }

        internal ShaderResourceView GetTexture3(MaterialManager materialManager)
        {
            return materialManager.Textures[materialManager.GetTextureID(this.textureName3)];
        }

        internal short GetTextureIDBumbTexture1(MaterialManager materialManager)
        {
            return (short)this.bumpTextureID1;
        }

        internal short GetTextureIDBumbTexture2(MaterialManager materialManager)
        {
            return (short)this.bumpTextureID2;
        }

        internal short GetTextureIDBumbTexture3(MaterialManager materialManager)
        {
            return (short)this.bumpTextureID3;
        }

        internal short GetTextureIDTexture1(MaterialManager materialManager)
        {
            return (short)this.TextureID1;
        }

        internal short GetTextureIDTexture2(MaterialManager materialManager)
        {
            return (short)this.TextureID2;
        }

        internal short GetTextureIDTexture3(MaterialManager materialManager)
        {
            return (short)this.TextureID3;
        }

        #endregion Internal Methods

        #region Private Methods

        private void UpdateHashCode()
        {
            AdlerChecksum adlerChecksum = new AdlerChecksum();
            byte[] data = new byte[40];

            BitConverter.GetBytes(this.textureName1.GetHashCode()).CopyTo(data, 0);
            BitConverter.GetBytes(this.textureName2.GetHashCode()).CopyTo(data, 4);
            BitConverter.GetBytes(this.textureName3.GetHashCode()).CopyTo(data, 8);
            BitConverter.GetBytes(this.mappingsWr2_1.CRC).CopyTo(data, 12);
            BitConverter.GetBytes(this.mappingsWr2_2.CRC).CopyTo(data, 16);
            BitConverter.GetBytes(this.mappingsWr2_3.CRC).CopyTo(data, 20);
            BitConverter.GetBytes((int)this.mappingMode).CopyTo(data, 24);
            BitConverter.GetBytes(this.bumpTextureName1.GetHashCode()).CopyTo(data, 28);
            BitConverter.GetBytes(this.bumpTextureName2.GetHashCode()).CopyTo(data, 32);
            BitConverter.GetBytes(this.bumpTextureName3.GetHashCode()).CopyTo(data, 36);
            adlerChecksum.MakeForBuff(data);

            this.hashCode = adlerChecksum.ChecksumValue;//
        }

        private void UpdateTextureIDs()
        {
            this.TextureID1 = this.materialManager.GetTextureID(this.textureName1);
            this.TextureID2 = this.materialManager.GetTextureID(this.textureName2);
            this.TextureID3 = this.materialManager.GetTextureID(this.textureName3);
            this.bumpTextureID1 = this.materialManager.GetTextureID(this.bumpTextureName1);
            this.bumpTextureID2 = this.materialManager.GetTextureID(this.bumpTextureName2);
            this.bumpTextureID3 = this.materialManager.GetTextureID(this.bumpTextureName3);
        }

        #endregion Private Methods
    }

    public class C11MaterialTextureIDs
    {
    }
}